﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web.Hosting;
using IronPython.Hosting;
using IronPython.Runtime;
using Microsoft.Scripting.Hosting;

namespace LynxWikiScripting
{
    public class WikiScriptEngine
    {
        private static readonly Lazy<WikiScriptEngine> scriptEngine = new Lazy<WikiScriptEngine>(() => { return new WikiScriptEngine(); }, true);

        private WikiScriptEngine() {}

        private static string exclusionRegexString = @"imp|eval|exec[^ute]|memoryview|path";
        private static Regex exclusionRegex = new Regex(exclusionRegexString, RegexOptions.Compiled | RegexOptions.Multiline);

        
        public static ScriptEngine Engine = Python.CreateEngine(new Dictionary<string, object>() {
                { "Frames", true }, { "FullFrames", true }, { "ExceptionDetail", true }, { "ShowClrExceptions", true }, { "MTA", true }} );
        public static ScriptRuntime Runtime = Engine.Runtime;
        public static ScriptScope scope = Engine.CreateScope();
        public static string code_path = HostingEnvironment.ApplicationPhysicalPath + @"App_Code";
        public static bool srchSet = false;

        public static void Initialize()
        {
            if (!srchSet) {
                List<string> srchPaths = Engine.GetSearchPaths().ToList();
                srchPaths.Add(code_path);
                Engine.SetSearchPaths(srchPaths);
                Engine.GetBuiltinModule();
            }
        }

        //scriptSource.AppendLine("import PythonRandom");
        //scriptSource.AppendLine("from PythonRandom import random");

        public static string ExecuteBehavior(string scriptInput)
        {
            if (exclusionRegex.IsMatch(scriptInput)) {
                return "IronWiki: IronWiki - Script may not include 'imp | eval | exec | memoryview | path'";
            }

            StringBuilder scriptSource = new StringBuilder();
            scriptSource.AppendLine("import sys");
            scriptSource.AppendLine(@"sys.path.append(r'C:\Program Files (x86)\IronPython 2.7\Lib')");
            scriptSource.AppendLine(@"sys.path.append(r'C:\Program Files (x86)\IronPython 2.7\DLLs')");
            scriptSource.AppendLine("import clr");
            scriptSource.AppendLine("import warnings, contextlib, future_builtins, abc, atexit, traceback, inspect, site");
            scriptSource.AppendLine("import string, re, difflib");
            scriptSource.AppendLine("import datetime, calendar, collections, heapq, bisect, array, sched, Queue, weakref, types, copy, pprint, repr");
            scriptSource.AppendLine("import numbers, math, decimal, fractions, random, itertools, functools, operator");
            scriptSource.AppendLine("import linecache");
            scriptSource.AppendFormat(@"clr.AddReferenceToFileAndPath(r'{0}')", code_path + @"\IronWiki.dll");
            scriptSource.AppendLine();
            scriptSource.AppendLine("clr.AddReference('IronPython.SQLite')");
            scriptSource.AppendLine("import anydbm, whichdb, dumbdbm, sqlite3");
            scriptSource.AppendLine("import csv");
            scriptSource.AppendLine("import hashlib, hmac");
            scriptSource.AppendLine("import time, platform");
            scriptSource.AppendLine("import json");
            scriptSource.AppendLine("import IronWiki");
            scriptSource.AppendLine("from IronWiki import UtilityProperties");
            scriptSource.AppendLine("from IronWiki import IronWikiSyntax");
            scriptSource.AppendLine("from IronWiki import WikiTopic, WikiSearch, WikiDirectory, WikiRequest");
            scriptInput = scriptInput.Replace("&lt;", "<");
            scriptInput = scriptInput.Replace("&gt;", ">");
            scriptSource.Append(scriptInput.Replace("&amp;", "&"));
            try {
                ScriptSource source = Engine.CreateScriptSourceFromString(scriptSource.ToString(), Microsoft.Scripting.SourceCodeKind.Statements);
                var res = (string)source.Execute(scope);
                var varPyNow = String.Empty;
                scope.TryGetVariable<string>("ScriptOutput", out varPyNow);
                return varPyNow;
            }
            catch (Exception ex) {
                Debug.Print(ex.Message);
                return ex.Message;
            }
        }
    }
}
